package com.zhucai.credit.bank.icbc.service;

import java.io.IOException;
import java.io.StringReader;
import java.io.UnsupportedEncodingException;
import java.net.SocketException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.annotation.Resource;

import org.apache.commons.codec.binary.Base64;
import org.jdom2.Element;
import org.jdom2.JDOMException;
import org.jdom2.input.SAXBuilder;
import org.jsoup.nodes.Document;
import org.jsoup.select.Elements;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.thymeleaf.TemplateEngine;
import org.thymeleaf.context.Context;
import org.xml.sax.InputSource;

import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Maps;
import com.zhucai.credit.bank.icbc.bean.GylinfoupSend;
import com.zhucai.credit.bank.icbc.bean.QgylinfoReturn;
import com.zhucai.credit.bank.icbc.bean.QgylinfoSend;
import com.zhucai.credit.bank.icbc.bean.QgylinstrReturn;
import com.zhucai.credit.bank.icbc.bean.QgylinstrSend;
import com.zhucai.credit.bank.log.LogService;
import com.zhucai.credit.exception.SubmitCcbException;
import com.zhucai.credit.monitor.service.INotificationService;
import com.zhucai.credit.utils.FormatTransfUtils;
import com.zhucai.credit.utils.StrongJsoup;

/**
 * 工行数据封装接口
 * 
 * @author xjz
 */
@Service
public class IcbcService {

	private final String tpType = "icbc.r";

	@Autowired
	private TemplateEngine templateEngine;
	@Autowired
	private LogService logService;
	@Resource(name="SMS")
	private INotificationService iNotificationService;

	/**
	 * 集团CIS号
	 */
	@Value("${icbc.cis}")
	private String cis;

	/**
	 * 归属银行编号
	 */
	@Value("${icbc.bank_code}")
	private String bankCode;

	/**
	 * 证书ID
	 */
	@Value("${icbc.id}")
	private String id;

	/**
	 * 版本号
	 */
	@Value("${icbc.version}")
	private String version;

	/**
	 * 发送路径
	 */
	@Value("${icbc.url}")
	private String url;

	/**
	 * 验签路径
	 */
	@Value("${icbc.sign_url}")
	private String signUrl;

	/**
	 * 应付账款信息查询接口 QGYLINFO
	 * 
	 * @param infoSend
	 * @return
	 * @throws Exception
	 */
	public List<QgylinfoReturn> getQgylinfoReturnList(QgylinfoSend infoSend) throws Exception {
		Date d = new Date();
		SimpleDateFormat fmtTranDate = new SimpleDateFormat("yyyyMMdd");
		SimpleDateFormat fmtTranTime = new SimpleDateFormat("HHmmssSSS");

		String CODE = "QGYLINFO";
		String tranDate = fmtTranDate.format(d);
		String tranTime = fmtTranTime.format(d);
		// 保存日志
		Long logId = logService.getLog(tpType, CODE);
		long fSeqno = logId;

		// 封装提交参数 ----start
		Context context = new Context();
		context.setVariable("CIS", cis);
		context.setVariable("BankCode", bankCode);
		context.setVariable("ID", id);
		context.setVariable("TranDate", tranDate);
		context.setVariable("TranTime", tranTime);
		context.setVariable("fSeqno", fSeqno);

		context.setVariable("AccNo", infoSend.getAccNo());
		context.setVariable("MemberAccNo", infoSend.getMemberAccNo());
		context.setVariable("GYLCode", infoSend.getGylCode());
		context.setVariable("MemberType", infoSend.getMemberType());
		context.setVariable("MemberCode", infoSend.getMemberCode());
		context.setVariable("Status", infoSend.getStatus());
		context.setVariable("BeginDate", infoSend.getBeginDate());
		context.setVariable("EndDate", infoSend.getEndDate());
		context.setVariable("NextTag", infoSend.getNextTag());

		// -------end
		String x = "<?xml version=\"1.0\" encoding=\"GBK\"?>" + templateEngine.process("icbc/" + CODE, context);
		// 更新日志信息
		logService.requestLog(logId, x);

		String response = null;
		try {
			response = getReponse(fSeqno, CODE, tranDate, tranTime, x);
			logService.responseLog(logId, response, logId + "");
			Element out = getTxInfo(response);
			List<QgylinfoReturn> returnList = new ArrayList<>();
			List<Element> children = out.getChildren("rd");
			for (Element e : children) {
				// 封装返回参数
				QgylinfoReturn returnInfo = new QgylinfoReturn();
				returnInfo.setAccNo(out.getChildText("AccNo"));
				returnInfo.setMemberAccNo(out.getChildText("MemberAccNo"));
				returnInfo.setGylCode(out.getChildText("GYLCode"));
				returnInfo.setMemberType(out.getChildText("MemberType"));
				returnInfo.setMemberCode(out.getChildText("MemberCode"));
				returnInfo.setStatus(out.getChildText("Status"));
				returnInfo.setAccCis(out.getChildText("AccCIS"));
				returnInfo.setBeginDate(out.getChildText("BeginDate"));
				returnInfo.setEndDate(out.getChildText("EndDate"));
				returnInfo.setNextTag(out.getChildText("NextTag"));

				returnInfo.setDueNo(e.getChild("DueNo").getValue());
				returnInfo.setFullName(e.getChild("FullName").getValue());
				returnInfo.setMemberName(e.getChild("MemberName").getValue());
				returnInfo.setMemberCIS(e.getChild("MemberCIS").getValue());
				returnInfo.setMemberRecAcc(e.getChild("MemberRecAcc").getValue());
				returnInfo.setDueAmt(FormatTransfUtils.stringToLong(e.getChild("DueAmt").getValue()));
				returnInfo.setDueBal(FormatTransfUtils.stringToLong(e.getChild("DueBal").getValue()));
				returnInfo.setDueDate(e.getChild("DueDate").getValue());
				returnInfo.setPlanDate(e.getChild("PlanDate").getValue());
				returnInfo.setStatus(e.getChild("Status").getValue());
				returnInfo.setVouchType(e.getChild("VouchType").getValue());
				returnInfo.setVouchCode(e.getChild("VouchCode").getValue());
				returnInfo.setVouchNo(e.getChild("VouchNo").getValue());
				returnList.add(returnInfo);
			}

			return returnList;
		} catch (IOException e) {
			logService.responseLog(logId, response, logId + "");
			iNotificationService.notification("### 工行网关异常");
			throw new SocketException();
		} catch (SubmitCcbException e) {
			logService.responseLog(logId, response, logId + "");
			throw new SubmitCcbException("连接工商银行失败", e);
		}
	}

	/**
	 * 应付账款信息录入修改删除接口 GYLINFOUP
	 * 
	 * @param send
	 * @return
	 * @throws Exception
	 */
	public Map<String, String> updateGylinoup(GylinfoupSend send) throws SocketException, SubmitCcbException {
		Date d = new Date();
		SimpleDateFormat fmtTranDate = new SimpleDateFormat("yyyyMMdd");
		SimpleDateFormat fmtTranTime = new SimpleDateFormat("HHmmssSSS");

		String CODE = "GYLINFOUP";
		String tranDate = fmtTranDate.format(d);
		String tranTime = fmtTranTime.format(d);
		Long logId = logService.getLog(tpType, CODE);
		long fSeqno = logId;
		Map<String, String> map = new HashMap<>();
		map.put("fSeqno", fSeqno + "");

		// 封装提交参数
		Context context = new Context();
		context.setVariable("CIS", cis);
		context.setVariable("BankCode", bankCode);
		context.setVariable("ID", id);
		context.setVariable("TranDate", tranDate);
		context.setVariable("TranTime", tranTime);
		context.setVariable("fSeqno", fSeqno);

		context.setVariable("TotalNum", send.getTotalNum());
		context.setVariable("TotalAmt", send.getTotalAmt());
		context.setVariable("AccNo", send.getAccNo());
		context.setVariable("TranType", send.getTranType());
		context.setVariable("GYLCode", send.getGylCode());
		context.setVariable("SignTime", tranDate + tranTime);

		context.setVariable("iSeqno", System.currentTimeMillis());
		context.setVariable("VouchType", send.getVouchType());
		context.setVariable("VouchCode", send.getVouchCode());
		context.setVariable("VouchNo", send.getVouchNo());
		context.setVariable("BillAmt", send.getBillAmt());
		context.setVariable("DueAmt", send.getDueAmt());
		context.setVariable("BillNo", send.getBillNo());
		context.setVariable("CrvouhType", send.getCrvouhType());
		context.setVariable("CrvouhCode", send.getCrvouhCode());
		context.setVariable("CrvouhNo", send.getCrvouhNo());
		context.setVariable("ContactNo", send.getContactNo());
		context.setVariable("DueDate", send.getDueDate());
		context.setVariable("DueCode", send.getDueCode());
		context.setVariable("PlanDate", send.getPlanDate());
		context.setVariable("MemberName", send.getMemberName());
		context.setVariable("MemberAccNo", send.getMemberAccNo());
		context.setVariable("MemberType", send.getMemberType());
		context.setVariable("MemberCode", send.getMemberCode());
		context.setVariable("Notes", send.getNotes());
		context.setVariable("Status", send.getStatus());
		context.setVariable("DelayDay", send.getDelayDay());

		String response = null;
		try {
			StrongJsoup connect = StrongJsoup.connect(signUrl);
			String x = "<?xml version=\"1.0\" encoding=\"GBK\"?>\r\n" + templateEngine.process("icbc/" + CODE, context);
			logService.requestLog(logId, x);

			connect.headers(ImmutableMap.of("Content-Type", "INFOSEC_SIGN/1.0", "Content-Length", x.getBytes().length + ""));

			connect.getJsoupConn().postDataCharset("GBK").request().requestBody(x);

			Document post1 = connect.post(1);
			Elements eles = post1.getElementsByTag("sign");
			String signText = eles.text();
			response = getReponse(fSeqno, CODE, tranDate, tranTime, signText);
			logService.responseLog(logId, response, logId + "");
			Element pub = getTxInfo(response);
			// 封装返回数据
			if (pub != null) {
				map.put("TransCode", pub.getChildText("TransCode"));
				map.put("CIS", pub.getChildText("CIS"));
				map.put("BankCode", pub.getChildText("BankCode"));
				map.put("ID", pub.getChildText("ID"));
				map.put("TranDate", pub.getChildText("TranDate"));
				map.put("TranTime", pub.getChildText("TranTime"));
				if (pub.getChildText("fSeqno") != null && !pub.getChildText("fSeqno").equals("")) {
					map.put("fSeqno", pub.getChildText("fSeqno"));
				}
				map.put("SerialNo", pub.getChildText("SerialNo"));
				map.put("RetCode", pub.getChildText("RetCode"));
				map.put("RetMsg", pub.getChildText("RetMsg"));
			}

			return map;
		} catch (IOException e) {
			logService.responseLog(logId, response, logId + "");
			iNotificationService.notification("### 工行网关异常");
			throw new SocketException();
		} catch (SubmitCcbException e) {
			logService.responseLog(logId, response, logId + "");
			throw new SubmitCcbException("连接银行失败", e);
		}
	}

	/**
	 * 应付账款指令查询接口 QGYLINSTR
	 * 
	 * @param send
	 * @return
	 * @throws Exception
	 */
	public List<QgylinstrReturn> getQgylinstrReturnList(QgylinstrSend send) throws Exception {

		Date d = new Date();
		SimpleDateFormat fmtTranDate = new SimpleDateFormat("yyyyMMdd");
		SimpleDateFormat fmtTranTime = new SimpleDateFormat("HHmmssSSS");

		String CODE = "QGYLINSTR";
		String tranDate = fmtTranDate.format(d);
		String tranTime = fmtTranTime.format(d);
		// 保存日志
		Long logId = logService.getLog(tpType, CODE);
		long fSeqno = logId;
		// 封装提交参数
		Context context = new Context();
		context.setVariable("CIS", cis);
		context.setVariable("BankCode", bankCode);
		context.setVariable("ID", id);
		context.setVariable("TranDate", tranDate);
		context.setVariable("TranTime", tranTime);
		context.setVariable("fSeqno", fSeqno);

		context.setVariable("QryfSeqno", send.getQryfSeqno());
		context.setVariable("QrySerialNo", send.getQrySerialNo());

		context.setVariable("QryiSeqno", send.getQryiSeqno());
		context.setVariable("QryOrderNo", send.getQryOrderNo());
		// 提交参数格式化成xml形式
		String x = "<?xml version=\"1.0\" encoding=\"GBK\"?>" + templateEngine.process("icbc/" + CODE, context);
		// 更新日志信息
		logService.requestLog(logId, x);
		String response = null;
		try {
			// 提交路径
			response = getReponse(fSeqno, CODE, tranDate, tranTime, x);
			logService.responseLog(logId, response, logId + "");
			// 使用jdom获取目标元素
			Element out = getTxInfo(response);
			List<QgylinstrReturn> returnList = new ArrayList<>();
			List<Element> children = out.getChildren("rd");
			for (Element e : children) {
				// 封装返回参数
				QgylinstrReturn returnInfo = new QgylinstrReturn();
				returnInfo.setQryfSeqno(out.getChildText("QryfSeqno"));
				returnInfo.setQrySerialNo(out.getChildText("QrySerialNo"));
				returnInfo.setRepReserved1(out.getChildText("RepReserved1"));
				returnInfo.setRepReserved2(out.getChildText("RepReserved1"));

				returnInfo.setiSeqno(e.getChild("iSeqno").getValue());
				returnInfo.setQryiSeqno(e.getChild("QryiSeqno").getValue());
				returnInfo.setQryOrderNo(e.getChild("QryOrderNo").getValue());
				returnInfo.setVouchType(e.getChild("VouchType").getValue());
				returnInfo.setVouchCode(e.getChild("VouchCode").getValue());
				returnInfo.setVouchNo(e.getChild("VouchNo").getValue());
				returnInfo.setBillAmt(FormatTransfUtils.stringToLong(e.getChild("BillAmt").getValue()));
				returnInfo.setDueAmt(FormatTransfUtils.stringToLong(e.getChild("DueAmt").getValue()));
				returnInfo.setBillNo(e.getChild("BillNo").getValue());
				returnInfo.setContactNo(e.getChild("ContactNo").getValue());
				returnInfo.setDueDate(e.getChild("DueDate").getValue());
				returnInfo.setDueCode(e.getChild("DueCode").getValue());
				returnInfo.setPlanDate(e.getChild("PlanDate").getValue());
				returnInfo.setMemberName(e.getChild("MemberName").getValue());
				returnInfo.setMemAccNo(e.getChild("MemAccNo").getValue());
				returnInfo.setMemberType(e.getChild("MemberType").getValue());
				returnInfo.setMemberCode(e.getChild("MemberCode").getValue());
				returnInfo.setStatus(e.getChild("Status").getValue());
				returnInfo.setDelayDay(e.getChild("DelayDay").getValue());
				returnInfo.setResult(e.getChild("Result").getValue());
				returnInfo.setInstrRetCode(e.getChild("instrRetCode").getValue());
				returnInfo.setInstrRetMsg(e.getChild("instrRetMsg").getValue());
				returnInfo.setiRetCode(e.getChild("iRetCode").getValue());
				returnInfo.setiRetMsg(e.getChild("iRetMsg").getValue());
				returnInfo.setRepReserved3(e.getChild("RepReserved3").getValue());
				returnInfo.setRepReserved4(e.getChild("RepReserved4").getValue());
				returnList.add(returnInfo);
			}
			return returnList;
		} catch (IOException e) {
			logService.responseLog(logId, response, logId + "");
			iNotificationService.notification("### 工行网关异常");
			throw new SocketException();
		} catch (SubmitCcbException e) {
			logService.responseLog(logId, response, logId + "");
			throw new SubmitCcbException("连接银行失败", e);
		}
	}

	/**
	 * 获取工行返回的数据信息
	 * 
	 * @param fSeqno
	 * @param CODE
	 * @param tranDate
	 * @param tranTime
	 * @param x
	 * @return
	 * @throws UnsupportedEncodingException
	 * @throws SocketException
	 */
	private String getReponse(long fSeqno, String CODE, String tranDate, String tranTime, String x) throws UnsupportedEncodingException, SocketException {
		String sendUrl = url + "?userID=" + id + "&PackageID=" + fSeqno + "&SendTime=" + tranDate + tranTime;
		StrongJsoup connect = StrongJsoup.connect(sendUrl);
		connect.headers(ImmutableMap.of("Content-Type", "application/x-www-form-urlencoded"));

		Map<String, String> params = Maps.newHashMap();
		params.put("Version", version);
		params.put("TransCode", CODE);
		params.put("BankCode", bankCode);
		params.put("GroupCIS", cis);
		params.put("ID", id);
		params.put("Cert", "");
		params.put("reqData", x);
		connect.getJsoupConn().data(params);

		Document post = connect.post();
		String text = post.text();

		Base64 base64 = new Base64();
		String substring = text.substring(text.indexOf("=") + 1);
		byte[] decode = base64.decode(substring);
		String response = new String(decode, "GBK");
		return response;
	}

	/**
	 * 获取元素内容
	 * 
	 * @param response
	 * @return
	 */
	private Element getTxInfo(String response) {
		SAXBuilder saxBuilder = new SAXBuilder();
		try {
			StringReader read = new StringReader(response.trim());

			InputSource source = new InputSource(read);
			org.jdom2.Document doc = saxBuilder.build(source);
			Element root = doc.getRootElement();
			Element eb = root.getChild("eb");
			Element pub = eb.getChild("pub");
			String returnCode = pub.getChildText("RetCode");
			if (!"0".equals(returnCode)) {
				// 有错，抛出异常
				String returnMsg = pub.getChildText("RetMsg");
				throw new RuntimeException(returnMsg);
			}
			Element out = eb.getChild("out");
			if (out != null) {
				return out;
			} else {
				return pub;
			}
		} catch (JDOMException e) {
			throw new RuntimeException(e);
		} catch (IOException e) {
			throw new RuntimeException(e);
		}
	}

}
